package com.lkdont.android.media.camera.widget

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.SurfaceTexture
import android.opengl.EGL14
import android.opengl.GLSurfaceView
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.AttributeSet
import com.lkdont.android.base.util.Logger
import com.lkdont.android.base.util.PermissionUtils
import com.lkdont.android.camera.R
import com.lkdont.android.media.camera.CameraException
import com.lkdont.android.media.camera.CameraVideoRecorder
import com.lkdont.android.media.camera.OldCameraManager
import com.lkdont.android.media.opengl.FrameProgram
import java.lang.ref.WeakReference
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10

/**
 * 摄像头预览界面
 *
 * @author lkdont
 */
class CameraPreviewView(context: Context?, attrs: AttributeSet? = null) :
    GLSurfaceView(context, attrs) {

    companion object {

        private const val TAG = "CameraPreviewView"

        private val PERMISSIONS = arrayOf(
            Manifest.permission.CAMERA
        )
    }

    private val cameraPreviewRenderer: CameraPreviewRenderer

    /*** 摄像头方向 */
    var cameraOrientation: OldCameraManager.CameraOrientation =
        OldCameraManager.CameraOrientation.DEGREES_0
        set(value) {
            field = value
            OldCameraManager.instance.orientation = value
        }

    /*** 画面拉伸类型 */
    var scaleType: Int = FrameProgram.SCALE_FIX_XY
        set(value) {
            field = value
            cameraPreviewRenderer.scaleType = value
        }

    init {
        // 使用EGL版本2
        setEGLContextClientVersion(2)
        // 设置渲染器
        cameraPreviewRenderer = CameraPreviewRenderer(CameraHandler(this))
        setRenderer(cameraPreviewRenderer)
        renderMode = RENDERMODE_WHEN_DIRTY
        // 读取参数
        val ta = context?.obtainStyledAttributes(attrs, R.styleable.CameraPreviewView)
        // 摄像头方向
        cameraOrientation =
            when (ta?.getInt(R.styleable.CameraPreviewView_cpvCameraOrientation, 0)) {
                90 -> OldCameraManager.CameraOrientation.DEGREES_90
                180 -> OldCameraManager.CameraOrientation.DEGREES_180
                270 -> OldCameraManager.CameraOrientation.DEGREES_270
                else -> OldCameraManager.CameraOrientation.DEGREES_0
            }
        // 画面拉伸
        scaleType =
            ta?.getInt(R.styleable.CameraPreviewView_cpvScaleType, FrameProgram.SCALE_FIX_XY)
                ?: FrameProgram.SCALE_FIX_XY
        // 回收
        ta?.recycle()
    }

    var eventListener: EventListener? = null

    /*** 开始预览 */
    fun startPreview(cameraId: Int) {
        // 检查权限
        if (!PermissionUtils.hasPermissions(context, PERMISSIONS)) {
            eventListener?.onError(CameraException("没有权限"))
            return
        }
        // 检查摄像头是否已经打开
        if (OldCameraManager.instance.isOpened()) {
            eventListener?.onError(CameraException("摄像头已经开启"))
            return
        }
        // 打开摄像头
        if (!OldCameraManager.instance.openCamera(cameraId)) {
            eventListener?.onError(CameraException("打开摄像头失败"))
            return
        }
        // 设置渲染器
        cameraPreviewRenderer.scaleType = scaleType
        cameraPreviewRenderer.recorder = recorder
        cameraPreviewRenderer.setVideoFrameSize(
            OldCameraManager.instance.previewWidth,
            OldCameraManager.instance.previewHeight
        )
        val surfaceTexture = cameraPreviewRenderer.surfaceTexture
        if (surfaceTexture != null) {
            setCameraPreview(surfaceTexture)
        }
        // 同步渲染器状态
        onResume()
        // 事件回调
        eventListener?.onPreviewStart()
    }

    /*** 停止预览 */
    fun stopPreview() {
        OldCameraManager.instance.closeCamera()
        // 同步渲染器状态
        queueEvent {
            cameraPreviewRenderer.stop()
        }
        onPause()
        // 事件回调
        eventListener?.onPreviewStop()
    }

    private fun setCameraPreview(surface: SurfaceTexture?) {
        if (surface != null) {
            OldCameraManager.instance.startPreview(surface)
            Logger.d(
                this, "setCameraPreview: 开始摄像头预览 " +
                        "width=${OldCameraManager.instance.previewWidth}, " +
                        "height=${OldCameraManager.instance.previewHeight}"
            )
        } else {
            OldCameraManager.instance.stopPreview()
            Logger.d(this, "setCameraPreview: 停止摄像头预览")
        }
    }

    var recorder: CameraVideoRecorder? = null
        set(value) {
            field = value
            cameraPreviewRenderer.recorder = value
        }

    //******************************** 子类 ********************************//

    /*** 事件监听器 */
    interface EventListener {

        /*** 预览开始 */
        fun onPreviewStart()

        /*** 预览停止 */
        fun onPreviewStop()

        /*** 发生错误 */
        fun onError(e: CameraException)

    }

    /*** 预览界面与渲染器的通讯Handler */
    private class CameraHandler(view: CameraPreviewView) : Handler(Looper.getMainLooper()) {

        companion object {
            const val WHAT_SET_PREVIEW = 101
            const val WHAT_ON_FRAME_AVAILABLE = 102
        }

        private val reference = WeakReference(view)

        fun postSetCameraPreview(surfaceTexture: SurfaceTexture) {
            val msg = obtainMessage(WHAT_SET_PREVIEW)
            msg.obj = surfaceTexture
            sendMessage(msg)
        }

        fun postOnFrameAvailable() {
            sendEmptyMessage(WHAT_ON_FRAME_AVAILABLE)
        }

        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            val view = reference.get() ?: return
            when (msg.what) {
                WHAT_SET_PREVIEW -> {
                    view.setCameraPreview(msg.obj as SurfaceTexture)
                }
                WHAT_ON_FRAME_AVAILABLE -> {
                    view.requestRender()
                }
            }
        }
    }

    /**
     * 摄像头预览渲染器
     *
     * @author lkdont
     */
    private class CameraPreviewRenderer(
        private val cameraHandler: CameraHandler
    ) : Renderer {

        var recorder: CameraVideoRecorder? = null

        /*** 停止渲染 */
        fun stop() {
            // 释放SurfaceTexture
            releaseSurfaceTexture()
            // 释放FrameProgram
            releaseProgram()
        }

        /*** 现实尺寸 */
        private var displayWidth: Int = 0
        private var displayHeight: Int = 0

        /*** 视频帧尺寸 */
        private var frameWidth: Int = 0
        private var frameHeight: Int = 0

        /*** 拉伸类型 */
        var scaleType: Int = FrameProgram.SCALE_FIX_XY
            set(value) {
                field = value
                mProgram?.scaleType = value
            }

        fun setVideoFrameSize(width: Int, height: Int) {
            frameWidth = width
            frameHeight = height
            mProgram?.setVideoFrameSize(width, height)
        }

        private var mProgram: FrameProgram? = null

        /*** 创建Program */
        private fun createFrameProgram(): FrameProgram {
            if (mProgram != null) {
                releaseProgram()
            }
            mProgram = FrameProgram()
            mProgram!!.setDisplaySize(displayWidth, displayHeight)
            mProgram!!.setVideoFrameSize(frameWidth, frameHeight)
            mProgram!!.scaleType = scaleType
            return mProgram!!
        }

        /*** 释放Program */
        private fun releaseProgram() {
            mProgram?.release()
            mProgram = null
        }

        var surfaceTexture: SurfaceTexture? = null
            private set

        private val stMatrix = FloatArray(16)

        private val onFrameAvailableListener = SurfaceTexture.OnFrameAvailableListener {
            cameraHandler.postOnFrameAvailable()
        }

        @SuppressLint("Recycle")
        private fun createSurfaceTexture(id: Int): SurfaceTexture {
            if (surfaceTexture != null) {
                releaseSurfaceTexture()
            }
            surfaceTexture = SurfaceTexture(id)
            surfaceTexture!!.setOnFrameAvailableListener(onFrameAvailableListener)
            return surfaceTexture!!
        }

        private fun releaseSurfaceTexture() {
            surfaceTexture?.release()
            surfaceTexture = null
        }

        private var needUpdateRecordContext = false
        private var needSetRecordTextureId = false

        override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
            Logger.d(this, "onSurfaceCreated")
            // 创建Program
            val program = createFrameProgram()
            // 创建SurfaceTexture
            val surfaceTextureId = program.createTextureObject()
            val surfaceTexture = createSurfaceTexture(surfaceTextureId)
            // 通知摄像头设置预览
            cameraHandler.postSetCameraPreview(surfaceTexture)
            // 录制
            needUpdateRecordContext = true
            needSetRecordTextureId = true
        }

        override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
            Logger.d(this, "onSurfaceChanged: width=$width, height=$height")
            displayWidth = width
            displayHeight = height
            mProgram?.setDisplaySize(width, height)
        }

        override fun onDrawFrame(gl: GL10?) {
            // 更新视频帧数据
            surfaceTexture!!.updateTexImage()

            // 录制
            if (recorder?.isRecording() == true) {
                if (needUpdateRecordContext) {
                    needUpdateRecordContext = false
                    recorder!!.updateSharedContext(EGL14.eglGetCurrentContext())
                }
                if (needSetRecordTextureId) {
                    needSetRecordTextureId = false
                    recorder!!.setTextureId(mProgram!!.textureId)
                }
                recorder!!.inputVideoFrame(surfaceTexture!!)
            } else {
                needUpdateRecordContext = true
                needSetRecordTextureId = true
            }

            // 绘制视频帧
            surfaceTexture!!.getTransformMatrix(stMatrix)
            mProgram!!.texMatrix = stMatrix
            mProgram!!.draw()
        }
    }
}